package com.mazaiting.gateway.sentinel;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.mazaiting.common.core.domain.result.IResultCode;
import com.mazaiting.common.core.domain.result.Result;
import com.mazaiting.common.core.domain.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.net.URI;
import java.util.List;

/**
 * 自定义网关流控异常
 */
@Slf4j
@Configuration
public class SentinelConfiguration {

    /**
     * 配置限流后异常处理
     *
     * @return 处理器
     */
    @PostConstruct
    public void blockRequestHandler() {
        BlockRequestHandler blockRequestHandler = (exchange, t) -> {
//            // 判断是否是html访问，如果是则转发url
//            if (acceptsHtml(exchange)) {
//                return htmlErrorResponse();
//            }

            IResultCode resultCode = ResultCode.SENTINEL_ERROR;
            if (t instanceof FlowException flowException) {
                log.error("流量治理-系统限流, 规则: {}, 调用来源: {}", flowException.getRule(), flowException.getRuleLimitApp());
                resultCode = ResultCode.SENTINEL_FLOW_LIMITING;
            } else if (t instanceof DegradeException degradeException) {
                log.error("流量治理-系统功能降级, 规则: {}, 调用来源: {}", degradeException.getRule(), degradeException.getRuleLimitApp());
                resultCode = ResultCode.SENTINEL_DEGRADATION;
            } else if (t instanceof ParamFlowException paramFlowException) {
                log.error("流量治理-热点参数异常, 规则: {}, 调用来源: {}", paramFlowException.getRule(), paramFlowException.getRuleLimitApp());
                resultCode = ResultCode.SENTINEL_HOT_PARAM_FLOW;
            } else if (t instanceof SystemBlockException systemBlockException) {
                log.error("流量治理-系统异常, 规则: {}, 调用来源: {}", systemBlockException.getRule(), systemBlockException.getRuleLimitApp());
                resultCode = ResultCode.SENTINEL_SYSTEM_BLOCK;
            } else if (t instanceof AuthorityException authorityException) {
                log.error("流量治理-授权异常, 规则: {}, 调用来源: {}", authorityException.getRule(), authorityException.getRuleLimitApp());
                resultCode = ResultCode.SENTINEL_AUTHORITY;
            } else {
                log.error("流量治理-未知异常: {}", t.getMessage());
            }
            return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(Result.failed(resultCode)));
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }


    /**
     * 跳转失败页面
     * @return 响应
     */
    private Mono<ServerResponse> htmlErrorResponse() {
        String url="http://www.baidu.com";
        URI uri = URI.create(url);
        return ServerResponse.temporaryRedirect(uri).build();
    }

    /**
     * 判断是否为网页
     * @param exchange 交换机
     * @return true: 网页; fasle: 非网页
     */
    private boolean acceptsHtml(ServerWebExchange exchange) {
        try {
            List<MediaType> acceptedMediaTypes = exchange.getRequest().getHeaders().getAccept();
            acceptedMediaTypes.remove(MediaType.ALL);
            MediaType.sortBySpecificityAndQuality(acceptedMediaTypes);
            return acceptedMediaTypes.stream()
                    .anyMatch(MediaType.TEXT_HTML::isCompatibleWith);
        } catch (InvalidMediaTypeException ex) {
            log.error("无效媒体类型: {}", ex.getMediaType());
            return false;
        }
    }
}
